#ifndef  RESSOURCESLIST_HPP
# define RESSOURCESLIST_HPP

# include <vector>
# include <string>
# include "datatree.hpp"
# include <iostream>
# include <algorithm>

namespace Shiny
{
  template<typename T>
  class RessourceData
  {
    static RessourceData<T>* _instance;

    RessourceData(void)
    {
      _instance = this;
    }

    ~RessourceData(void)
    {
      while (_ressources.begin() != _ressources.end())
      {
        delete *(_ressources.begin());
        _ressources.erase(_ressources.begin());
      }
    }

    struct Ressource
    {
      T            ressource;
      std::string  path;
      unsigned int pointers;
    };

    typedef std::list<Ressource*> Ressources;
    
  public:
    // Singleton
    static RessourceData* Get(void)
    {
      return (_instance ? _instance : new RessourceData);
    }

    static void           Clean(void)
    {
      if (_instance)
        delete _instance;
      _instance = 0;
    }

    // Ressource Manager
    inline void SetDirectory(const std::string& path) { _directory = path; }

    const T* Load(const std::string& path)
    {
      typename Ressources::const_iterator it    = _ressources.begin();
      typename Ressources::const_iterator end   = _ressources.end();
      std::string                         fpath = _directory + "/" + path;

      for (; it != end ; ++it)
      {
        if ((*it)->path == fpath)
        {
	  std::cout << "RessourceList requested ressource " << fpath << std::endl;
          (*it)->pointers++;
          return (&((*it)->ressource));
        }
      }
      return (LoadFromFile(path));
    }

    void Delete(const T* pointer)
    {
      typename Ressources::iterator it  = _ressources.begin();
      typename Ressources::iterator end = _ressources.end();

      for (; it != end ; ++it)
      {
        if (&((*it)->ressource) == pointer)
        {
          (*it)->pointers--;
          if ((*it)->pointers == 0)
          {
            std::cout << "Ressource deleted from memory " << (*it)->path << std::endl;
            delete (*it);
            _ressources.erase(it);
            break ;
          }
        }
      }
    }

  private:
    const T* LoadFromFile(const std::string& path)
    {
      Ressource* ressource = new Ressource();

      if ((ressource->ressource.LoadFromFile(_directory + "/" + path)))
      {
        ressource->path = _directory + "/" + path;
        ressource->pointers++;
        _ressources.push_back(ressource);
	std::cout << "Ressource loaded in memory: " << ressource->path << " (" << &(ressource->ressource) << ")" << std::endl;
        return (&(ressource->ressource));
      }
      std::cout << "/!\\ No se puede cargar " << ressource->path << std::endl;
      delete ressource;
      return (0);
    }

    Ressources  _ressources;
    std::string _directory;
  };

  template<typename T> RessourceData<T>* RessourceData<T>::_instance = 0;

  /*
   * RessourceList
   */
  template<typename T>
  class RessourceList
  {
    struct Pair
    {
      Pair()                                                {}
      Pair(std::string f, const T* s) : first(f), second(s) {}

      bool operator==(const std::string& c) const { return (first == c);       }
      bool operator==(const Pair& c) const        { return (first == c.first); }

      std::string first;
      const T*    second;
    };

    typedef std::vector<Pair>                   Ressources;
    typedef typename Ressources::iterator       RessourcesIt;
    typedef typename Ressources::const_iterator RessourcesConstIt;

  public:
    RessourceList() {}
    
    RessourceList(Data list)
    {
      LoadFromData(list);
    }
    
    ~RessourceList()
    {
      Clear();
    }

    const T* operator[](const std::string& key) const
    {
      RessourcesConstIt res = find(_ressources.begin(), _ressources.end(), key);

      return ((res == _ressources.end()) ? (0) : (*res).second);
    }
    
    void Add(const std::string& key, const T* value)
    {
      _ressources.push_back(Pair(key, value));
    }
    
    void LoadFromData(Data list)
    {
      Data::iterator current = list.begin();
      Data::iterator last    = list.end();

      for (; current != last ; ++current)
      {
        const T*      ressource = RessourceData<T>::Get()->Load((*current).Value());

        if (!ressource)
	{
	  std::cout << "RessourceList will lack value " << (*current).Key() << std::endl;
          continue ;
	}
	Add((*current).Key(), ressource);
	std::cout << "RessourceList: added " << (*current).Key() << ": " << (*current).Value() << std::endl;
      }
    }
    
    void Clear(void)
    {
      RessourcesIt current;

      while ((current = _ressources.begin()) != _ressources.end())
      {
        RessourceData<T>::Get()->Delete((*current).second);
	_ressources.erase(current);
      }
    }

    std::vector<std::string> Keys(void) const
    {
      std::vector<std::string> array;

      array.resize(_ressources.size());
      for (unsigned int it = 0 ; it < array.size() ; ++it)
        array[it] = _ressources[it].first;
      return (array);
    }

  private:
    std::vector<Pair> _ressources;
  };
}

#endif
